package com.ddstation.onsitelaw.service;

import com.ddstation.common.util.CheckUtil;
import com.ddstation.common.util.IdGenerateUtil;
import com.ddstation.onsitelaw.entity.auditentity.*;
import com.ddstation.onsitelaw.mapper.*;
import com.ddstation.onsitelaw.model.auditmodel.ClinicAuditItemModel;
import com.ddstation.onsitelaw.utils.Constant;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.inject.Inject;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ClinicAuditServiceImpl implements ClinicAuditService {

    private static final String STOP_WORD = "集团|股份|有限|公司";

    private static final Logger LOGGER = LoggerFactory.getLogger(ClinicAuditService1.class);
    @Inject
    private DataDictionaryService dataDictionaryService;
    @Inject
    private ClinicAuditMapper clinicAuditMapper;
    @Inject
    private DtHealthManageInstitutionAuditMapper dtHealthManageInstitutionMapper;
    @Inject
    private DtHealthManageInstitutionDoctorMapper dtHealthManageInstitutionDoctorMapper;
    @Inject
    private DtInvsRuleWarningMapper dtInvsRuleWarningMapper;
    @Inject
    private MaInvsBaseDrugMapper maInvsBaseDrugMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<DtInvsViolationManageEntity> auditOutpatientMedical(DtHealthDataOutpatientMedicalEntity medical
            , String institutionId
            , DdDoctorEntity doctor) {
        String[] targetList = new String[]{"WangYaRu", "fuBingBing"};
        DtHealthManageInstitutionEntity institution = dtHealthManageInstitutionMapper.select(institutionId);
        // 医疗机构诊疗执业许可证诊疗科目
        String technicalOffices = institution.getTechnicalOffices();
        if (CheckUtil.isEmpty(technicalOffices)) {
       /*     wxMessageService.sendToStaff(targetList, "医疗机构["+institution.getName()+"]没有选择诊疗科目，请设置！",
                    null, null, null);*/
        }
        // 医师执业证书执业范围
        String scopePractice = doctor.getScopePractice();
        if (CheckUtil.isEmpty(scopePractice)) {
          /*  wxMessageService.sendToStaff(targetList, "医师["+doctor.getName()+"]没有选择执业范围，请设置！",
                    null, null, null);*/
        }
        // 医师身份证号码
        String idCard = doctor.getIdCard();
        if (CheckUtil.isEmpty(idCard)) {
        /*    wxMessageService.sendToStaff(targetList, "医师["+doctor.getName()+"]没有设置身份证号，请设置！",
                    "医生["+doctor.getName()+"]", null, null);*/
        }

        List<DtInvsViolationManageEntity> violations = new ArrayList<>();
        DtInvsViolationManageEntity violation;
        if (!CheckUtil.isEmpty(technicalOffices)) {
            if ((technicalOffices.indexOf("50") == -1 // 50:中医科
                    && technicalOffices.indexOf("52") == -1 // 52:中西医结合科
                    && technicalOffices.indexOf("02") == -1) // 02:全科医疗科
                    || technicalOffices.indexOf(".02") > 0) {
                // 审核病历诊断:医疗机构超范围诊疗
                violation = auditOutpatientMedical(medical, technicalOffices);
                if (!CheckUtil.isEmpty(violation)) {
                    violation.setMsgId("810001");
                    // 格式化病历诊断违规内容
                    formatViolationMsg(medical, violation);
                    violations.add(copy(violation));
                }
            }
        }
        // 审核病历诊断:医师超范围开具处方
        List<MaDataDictionaryEntity> scopePracticeList = dataDictionaryService.getCodeList(Constant.CODE_SCOPE_PRACTICE);
        // 执业范围对应诊疗科目
        List<MaDataDictionaryEntity> filterList = scopePracticeList.stream().filter(a -> (a.getValue() != null && a.getValue().equals(scopePractice))).collect(Collectors.toList());
        if (filterList != null && filterList.size() > 0) {
            String scopePractices = (filterList.get(0)).getData1();
            if (!CheckUtil.isEmpty(scopePractices)) {
                if ((scopePractices.indexOf("50") == -1 // 50:中医科
                        && scopePractices.indexOf("52") == -1 // 52:中西医结合科
                        && scopePractices.indexOf("02") == -1) // 02:全科医疗科
                        || scopePractices.indexOf(".02") > 0) {
                    violation = auditOutpatientMedical(medical, scopePractices);
                    if (!CheckUtil.isEmpty(violation)) {
                        violation.setMsgId("810002");
                        violation.setDepartment((filterList.get(0)).getFullName());
                        violation.setDoctorName(doctor.getName());
                        // 格式化病历诊断违规内容
                        formatViolationMsg(medical, violation);
                        violations.add(copy(violation));
                    }
                }
            }
        } else {
        /*    wxMessageService.sendToStaff(targetList, "ma_data_dictionary表中无执业范围scopePractice数据，请设置！",
                    null, null, null);*/
        }

/*        // 审核病历诊断:多个执业地点同时开具处方
        violation = multiPractice(medical, idCard);
        if (!CheckUtil.isEmpty(violation)) {
            violation.setMsgId("810003");
            violation.setDoctorName(doctor.getName());
            violation.setParams(institution.getName() + "、" + violation.getParams());
            // 格式化病历诊断违规内容
            formatViolationMsg(medical, violation);
            violations.add(copy(violation));
        }*/

        // 更新病历诊断违规内容
        replaceMedicalViolation(medical, violations);
        return violations;
    }

    /**
     * 更新病历诊断违规内容
     * 先删除再重新生成
     */
    private void replaceMedicalViolation(DtHealthDataOutpatientMedicalEntity medical,
                                         List<DtInvsViolationManageEntity> violations) {
        DtInvsViolationManageEntity w = new DtInvsViolationManageEntity();
        w.setInstitutionId(medical.getInstitutionId());
        w.setMedicalId(medical.getId());
        // 旧的消息队列状态更新成不显示
        List<Integer> targetIds = clinicAuditMapper.selectTargetIds(w);
        if (targetIds.size() > 0) {
            clinicAuditMapper.updateQueueStatus(medical.getInstitutionId(), 401, 3, targetIds);
            // 旧的异常行为每日check更新成已删除
            List<Integer> illegalIds = clinicAuditMapper.selectIllegalIds(medical.getInstitutionId(), 40, 401, targetIds);
            clinicAuditMapper.updateCheckDailyStatus(medical.getInstitutionId(), 40, 401, targetIds);
            // 旧的机构异常行为申诉更新成已删除
            if (illegalIds.size() > 0) {
                clinicAuditMapper.updateIllegalAppealStatus(illegalIds);
            }
        }
        // 旧的违规内容状态更新成已删除
        clinicAuditMapper.updateStatus(w);
        // 新生成违规内容
        if (violations.size() > 0) {
            //获取医疗机构所在卫监所，同时判断该卫监是否对所有处方违规不需要申诉，is_pres_appeal=0 不需要申诉 is_pres_appeal=1需要申诉
            String isPresAppeal = dtHealthManageInstitutionMapper.selectPresAppeal(medical.getInstitutionId());
            clinicAuditMapper.insertMedicalViolation(violations);
            // 新生成消息队列(用于首页地图展示)
            insertMessageQueue(medical, violations);
            // 新生成机构异常行为每日check
            List<DtInvsIllegalCheckDailyEntity> dailys = insertCheckDaily(medical, violations);
            //生成appeal表数据
            insertMedicalIllegalAppealHandle(dailys, isPresAppeal, violations);
			/*// 生成新的机构异常行为申诉
            if (isPresAppeal== "1") {
                insertIllegalAppeal(dailys);
            }else {
                //审方警告关闭时生成状态为“关闭”的appeal数据
                insertIllegalAppealClose(dailys);
            }*/
        }

    }


    private void insertMedicalIllegalAppealHandle(List<DtInvsIllegalCheckDailyEntity> dailys, String isPresAppeal, List<DtInvsViolationManageEntity> violations) {
        List<String> msgIds = new ArrayList<>();
        for (DtInvsIllegalCheckDailyEntity daily : dailys) {
            msgIds.add(daily.getMsgId());
        }
        for (DtInvsIllegalCheckDailyEntity daily : dailys) {
            for (DtInvsViolationManageEntity violation : violations) {
                if (Objects.equals(daily.getMsgId(), violation.getMsgId())) {
                    daily.setWarningFlag(violation.getWarningFlag());
                }
            }
        }
        //预警提醒的规则
        List<DtInvsIllegalCheckDailyEntity> checkDailyWarning = dailys.stream().filter(a -> a.getWarningFlag() == 1).
                collect(Collectors.toList());
        //关闭预警提醒的规则
        List<DtInvsIllegalCheckDailyEntity> checkDailyNotWarning = dailys.stream().filter(a -> a.getWarningFlag() == 0).
                collect(Collectors.toList());
        if (isPresAppeal == "0") {  //不预警
            insertIllegalAppealClose(dailys);
        } else {
            if (checkDailyNotWarning != null && checkDailyNotWarning.size() > 0) {
                insertIllegalAppealClose(checkDailyNotWarning);
            }
            if (checkDailyWarning != null && checkDailyWarning.size() > 0) {
                insertIllegalAppeal(checkDailyWarning);
            }
        }
    }
    /**
     *
     * 遍历同一个医生在不同执业地点同时开处方的机构名
     *
     */
/*    private DtInvsViolationManageEntity multiPractice(DtHealthDataOutpatientMedicalEntity medical, String idCard) {
        DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();

        Integer val = 2; // 间隔时间为至少2小时以上
        List<Map<String,Object>> sameList = clinicAuditMapper.selectSameDoctorMedical(medical.getInstitutionId(), idCard, val);
        if (sameList.size() > 0 ) {
            String param = "";
            for (Map<String,Object> map : sameList) {
                String str = (String)map.get("institutionName");
                if (CheckUtil.isEmpty(param)) {
                    param = str;
                } else {
                    param = param + "、" + str;
                }
            }
            violation.setParams(param);
            return violation;
        }

        return null;
    }*/

    /**
     * 审核病历诊断
     */
    private DtInvsViolationManageEntity auditOutpatientMedical(DtHealthDataOutpatientMedicalEntity medical,
                                                               String technicalOffices) {
        DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();

        List<String> technicals = new ArrayList<>();
        if (!CheckUtil.isEmpty(technicalOffices)) {
            technicals = Arrays.asList(technicalOffices.split(","));
        }

        // 没有先保存病历诊断
        if (CheckUtil.isEmpty(medical.getDiagnosis()) || CheckUtil.isEmpty(medical.getDiagnosisIcd10())) {
            return null;
        }

        List<String> icd10Codes = Arrays.asList(medical.getDiagnosisIcd10().split(","));
        List<String> diseases = Arrays.asList(medical.getDiagnosis().split(","));

        // 直接输入的诊断没有对应的ICD10疾病编码
        if (icd10Codes.size() != diseases.size()) {
            return null;
        }

        List<MaInvsIcd10SectionEntity> notExists = new ArrayList<>();

        for (int i = 0; i < icd10Codes.size(); i++) {
            if (i > 0) break; // 只对主诊断进行是否超范围校验
            Map<String, Object> param = new HashMap<String, Object>();
            List<String> icd10s = new ArrayList<>();
            icd10s.add(icd10Codes.get(i));
            param.put("icd10s", icd10s);
            List<MaInvsIcd10SectionEntity> sectionList = clinicAuditMapper.selectTechnicalsByIcd10(param);
            //查询该诊断是否预警
            List<MaInvsIcd10SectionEntity> noWarningList = clinicAuditMapper.selectByIcd10AndScope(technicalOffices, icd10Codes.get(i));
            if (sectionList != null && sectionList.size() > 0) {
                MaInvsIcd10SectionEntity section = sectionList.get(0);
                List<String> options = Arrays.asList(section.getOptionAll().split(","));

                // 连二级诊疗科目一起判断是否有交集
                if (getIntersection(technicals, options).size() > 0) {
                    continue;
                }

                // 去掉二级诊疗科目后判断是否有交集
                for (String option : options) {
                    Collections.replaceAll(options, option, option.split("\\.")[0]);
                }
                if (getIntersection(technicals, options).size() > 0) {
                    continue;
                }

                // icd10Codes、diseases的长度需一致，否则报错
                // 里面icd10编码和疾病顺序也需一致
                section.setDisease((String) diseases.get(i));
                if (noWarningList != null && noWarningList.size() > 0) {
                    section.setWarningFlag(0);
                } else {
                    section.setWarningFlag(1);
                }
                notExists.add(section);
            }
        }

        if (notExists.size() > 0) {
            // 医疗机构诊疗科目名录2012版(基础表数据)
            List<MaInvsIcd10SpecialtyEntity> specialtys = clinicAuditMapper.selectAllSpecialtys();

            String param = "";
            for (MaInvsIcd10SectionEntity section : notExists) {
                String str = ""; // 诊疗科目范围外科室或专业名称
                List<String> options = Arrays.asList(section.getOptionAll().split(","));
                for (String option : options) {
                    String name = "";
                    if (option.indexOf(".") > 0) {
                        List<MaInvsIcd10SpecialtyEntity> filterList = specialtys.stream().filter(a -> (a.getSecondCode() != null && a.getSecondCode().equals(option))).collect(Collectors.toList());
                        if (filterList != null && filterList.size() > 0) {
                            name = (filterList.get(0)).getFirstName()
                                    + "."
                                    + (filterList.get(0)).getSecondName();
                        }
                    } else {
                        List<MaInvsIcd10SpecialtyEntity> filterList = specialtys.stream().filter(a -> (a.getFirstCode() != null && a.getFirstCode().equals(option))).collect(Collectors.toList());
                        if (filterList != null && filterList.size() > 0) {
                            name = (filterList.get(0)).getFirstName();
                        }
                    }
                    if (CheckUtil.isEmpty(str)) {
                        str = name;
                    } else {
                        str = str + '或' + name;
                    }
                }
                str = section.getDisease() + "(" + str + ")";
                if (CheckUtil.isEmpty(param)) {
                    param = str;
                } else {
                    param = param + "、" + str;
                }
            }
            violation.setWarningFlag(notExists.get(0).getWarningFlag());
            violation.setParams(param);
            return violation;
        }

        return null;
    }

    /**
     * 判断两个list是否有交集
     */
    private List<String> getIntersection(List<String> list1,
                                         List<String> list2) {
        List<String> result = new ArrayList<>();
        for (String str : list2) {//遍历list1
            if (list1.contains(str)) {//如果存在这个值
                result.add(str);//放进一个list里面，这个list就是交集
            }
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<DtInvsViolationManageEntity> auditPrescriptions(DtHealthDataOutpatientMedicalEntity medical
            , String institutionId
            , DdDoctorEntity doctor
            , List<DtHealthDataWestPrescriptionEntity> prescriptions) {
        // 诊所科室(诊疗科目)
        DtHealthManageInstitutionEntity institution = dtHealthManageInstitutionMapper.select(institutionId);
        String technicalOffices = institution.getTechnicalOffices();
        DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();
        List<DtInvsViolationManageEntity> violations = new ArrayList<>();
        //----非执业医师开具处方-------
        DtHealthManageInstitutionDoctorEntity doctorEntity = new DtHealthManageInstitutionDoctorEntity();
        DtHealthManageInstitutionEntity institutionEntity = new DtHealthManageInstitutionEntity();
        institutionEntity.setId(institution.getId());
        doctorEntity.setId(doctor.getId());
        doctorEntity.setInstitution(institutionEntity);
        List<DtHealthManageInstitutionDoctorEntity> doctorList = dtHealthManageInstitutionDoctorMapper.select(doctorEntity);
        //根据机构id和医生id查询医生的角色（1:医生 2:护士 3:收费员 4:其他），除了医生无权开具处方
        if (doctorList != null && doctorList.size() > 0) {
            Integer role = doctorList.get(0).getRole();
            if (role != 1) {
                String param = "（非医生账号）"; // 职称名
                violation.setParams(param);
                violation.setPrescriptionId(prescriptions.get(0).getPrescriptionId());
                violation.setDoctorName(doctor.getName());
                violation.setMsgId("820001");
                formatViolationMsg(medical, violation);
                violations.add(copy(violation));
            }
        }
        //单个处方药品数不能大于5个
        List<DtHealthDataWestPrescriptionEntity> prescriptionList = prescriptions.stream().filter(a -> !StringUtils.isEmpty(a.getItemName())).collect(Collectors.toList());
        if (prescriptionList != null && prescriptionList.size() > 5) {
            String param = "违规开方";
            violation.setParams(param);
            violation.setPrescriptionId(prescriptions.get(0).getPrescriptionId());
            violation.setDoctorName(doctor.getName());
            violation.setMsgId("810004");
            formatViolationMsg(medical, violation);
            violations.add(copy(violation));
        }
        // 医师职称
		/*String technicalTitle = doctor.getTechnicalTitle();
		if (CheckUtil.isEmpty(technicalTitle)) {
			throw new RuntimeException("医师["+doctor.getName()+"]没有选择职称，请设置！");
		}*/
        List<ClinicAuditItemModel> items = new ArrayList<>();
        int i = 0;
        for (DtHealthDataWestPrescriptionEntity prescription : prescriptions) {
            if (StringUtils.isEmpty(prescription.getItemName())) {
                continue;
            }
            ClinicAuditItemModel item = new ClinicAuditItemModel();
            i++;
            String prescriptionName = "";
            prescriptionName = "西药处方";
            if (i > 1) prescriptionName = prescriptionName + 1;
            prescription.setPrescriptionName(prescriptionName);
            //根据药品名、规格、厂家匹配基本药品库药品数据
            String itemName = prescription.getItemName();
            //去掉药品名中可能存在的括号，再模糊查询
            if (prescription.getItemName().indexOf("(") > 0) {
                itemName = prescription.getItemName().substring(0, prescription.getItemName().indexOf("("));
            } else if (prescription.getItemName().indexOf("（") > 0) {
                itemName = prescription.getItemName().substring(0, prescription.getItemName().indexOf("（"));
            }
            List<MaInvsBaseDrugEntity> baseDrugs = maInvsBaseDrugMapper.selectByDrugName(itemName);
            if (baseDrugs != null && baseDrugs.size() > 0) {
                //药品名、厂商、规格、转换比不能为空
                List<MaInvsBaseDrugEntity> baseDrugList = baseDrugs.stream().filter(a -> (!StringUtils.isEmpty(a.getName()) && !StringUtils.isEmpty(a.getManufacturer())
                        && !StringUtils.isEmpty(a.getSpecification()) && a.getContentSpec() != null)).collect(Collectors.toList());
                List<Integer> ids = getMatchedIds(baseDrugList, prescription.getItemName(), prescription.getManufacturer(), prescription.getSpecification(), prescription.getConversionScale());
                if (ids != null && ids.size() > 0) {
                    List<MaInvsBaseDrugEntity> baseDrug = baseDrugList.stream().filter(a -> (a.getId() == ids.get(0))).collect(Collectors.toList());
                    item.setBaseDrug(baseDrug.get(0));
                    item.setWestItem(prescription);
                    items.add(item);
                }
            }
        }

        // 核心药品库至少匹配到一条
        if (items != null && items.size() > 0) {

            // 审核病历处方:无处方权开具处方、执业助理医师开具处方
            violation = rightToPrescription(doctor, items, institution.getId());
            if (!CheckUtil.isEmpty(violation)) {
                // 格式化病历处方违规内容
                formatViolationMsg(medical, violation);
                violations.add(copy(violation));
            }

            // 口腔诊所拔牙开具表面麻醉剂，不需要精麻资质
            if (!CheckUtil.isEmpty(technicalOffices) && technicalOffices.indexOf("12") == -1) { // 12:口腔科
                // 审核病历处方:未经批准开具精麻药品
                violation = rightToAnesthetic(doctor, items);
            }

            if (!CheckUtil.isEmpty(violation)) {
                // 格式化病历处方违规内容
                formatViolationMsg(medical, violation);
                violations.add(copy(violation));
            }

            // 审核病历处方:未经培训合格(未经核准)开具抗菌药物
            violation = rightToAntibiosis(doctor, items);
            if (!CheckUtil.isEmpty(violation)) {
                // 格式化病历处方违规内容
                formatViolationMsg(medical, violation);
                violations.add(copy(violation));
            }

            // 审核病历处方:越级使用抗菌药物
            violation = rightToAntibiosisGrade(doctor, items);
            if (!CheckUtil.isEmpty(violation)) {
                // 格式化病历处方违规内容
                formatViolationMsg(medical, violation);
                violations.add(copy(violation));
            }

            List<DtInvsViolationManageEntity> tempViolations = new ArrayList<>();
            // 审核病历处方:限制使用性别
            tempViolations = limitGender(items, medical.getGender());
            if (tempViolations != null && tempViolations.size() > 0) {
                for (DtInvsViolationManageEntity entity : tempViolations) {
                    // 格式化病历处方违规内容
                    formatViolationMsg(medical, entity);
                }
                violations.addAll(copys(tempViolations));
            }

            // 审核病历处方:儿童禁用
            tempViolations = childForbid(items, medical.getAge());
            if (tempViolations != null && tempViolations.size() > 0) {
                for (DtInvsViolationManageEntity entity : tempViolations) {
                    // 格式化病历处方违规内容
                    formatViolationMsg(medical, entity);
                }
                violations.addAll(copys(tempViolations));
            }

            // 审核病历处方:老年禁用
            tempViolations = oldForbid(items, medical.getAge());
            if (tempViolations != null && tempViolations.size() > 0) {
                for (DtInvsViolationManageEntity entity : tempViolations) {
                    // 格式化病历处方违规内容
                    formatViolationMsg(medical, entity);
                }
                violations.addAll(copys(tempViolations));
            }

            // 审核病历处方:超剂量不合理用药
            tempViolations = overDosage(items);
            if (tempViolations != null && tempViolations.size() > 0) {
                for (DtInvsViolationManageEntity entity : tempViolations) {
                    // 格式化病历处方违规内容
                    formatViolationMsg(medical, entity);
                }
                violations.addAll(copys(tempViolations));
            }
        }

        // 更新病历处方违规内容
        replacePrescriptionViolation(medical, prescriptions, violations, institution);
        return violations;
    }

    /**
     * 格式化病历诊断或病历处方违规内容
     */
    private DtInvsViolationManageEntity formatViolationMsg(DtHealthDataOutpatientMedicalEntity medical,
                                                           DtInvsViolationManageEntity violation) {
        String msgId = violation.getMsgId();
        String msg = "";
        net.sf.json.JSONObject jsonBean = new net.sf.json.JSONObject();

        if (msgId.equals("810001")) {
            // e.g. 病历诊断腹腔妊娠(妇产科)，超出核准登记诊疗科目开展诊疗活动。
            // 病历诊断{1}，超出核准登记诊疗科目开展诊疗活动。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getParams() + "\"}");

        } else if (msgId.equals("810002")) {
            // e.g. 病历诊断腹腔妊娠(妇产科)，儿科医师葫芦红超执业范围开具处方。
            // 病历诊断{1}，{2}医师{3}超执业范围开具处方。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getParams() +
                    "\",\"p2\":\"" + violation.getDepartment() +
                    "\",\"p3\":\"" + violation.getDoctorName() + "\"}");

        } else if (msgId.equals("810003")) {
            // e.g. 医师葫芦红在多个执业地点临安虚拟A诊所、临安虚拟B诊所同时开具处方。
            // 医师{1}在多个执业地点{2}同时开具处方。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getDoctorName() +
                    "\",\"p2\":\"" + violation.getParams() + "\"}");
        } else if (msgId.equals("820001")) {
            // e.g. 无处方权医师葫芦红(理疗师)开具处方。
            // 无处方权医生{1}开具处方。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getDoctorName() +
                    violation.getParams() + "\"}");
        } else if (msgId.equals("820002")) {
            // e.g. 执业助理医师葫芦红单独开具处方。
            // 执业助理医师{1}单独开具处方。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getDoctorName() + "\"}");
        } else if (msgId.equals("820003")) {
            // e.g. 医师葫芦红未经批准开具精麻药品双氢可待因片。
            // 医师{1}未经批准开具精麻药品{2}。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getDoctorName() +
                    "\",\"p2\":\"" + violation.getParams() + "\"}");
        } else if (msgId.equals("820004")) {
            // e.g. 医师葫芦红未经培训合格(未经核准)开具抗菌药物头孢丙烯。
            // 医师{1}未经培训合格(未经核准)开具抗菌药物{2}。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getDoctorName() +
                    "\",\"p2\":\"" + violation.getParams() + "\"}");
        } else if (msgId.equals("820005")) {
            // e.g. 医师葫芦红越级使用限制级抗菌药物头孢丙烯。
            // 医师{1}越级使用{2}。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getDoctorName() +
                    "\",\"p2\":\"" + violation.getParams() + "\"}");
        } else if (msgId.equals("840004")) {
            // e.g. 阿莫西林胶囊：超剂量用药。单次最大用量2片。
            // {1}：超剂量用药。最大用量{2}。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getItemName() +
                    "\",\"p2\":\"" + violation.getParams() + "\"}");
        } else if (msgId.equals("840005")) {
            // e.g. 醋酸甲羟孕酮片：仅限女性。
            // {1}：{2}。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getItemName() +
                    "\",\"p2\":\"" + violation.getParams() + "\"}");
        } else if (msgId.equals("840007")) {
            // e.g. 复方风湿宁片：儿童禁用。
            // {1}：{2}。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getItemName() +
                    "\",\"p2\":\"" + violation.getParams() + "\"}");
        } else if (msgId.equals("840008")) {
            // e.g. 兰索拉唑：老年患者禁用。
            // {1}：{2}。
            jsonBean = JSONObject.fromObject("{\"p1\":\"" + violation.getItemName() +
                    "\",\"p2\":\"" + violation.getParams() + "\"}");
        } else if (msgId.equals("810004")) {
            // e.g. 医师XX违规开方开具单个处方药品数目大于5。
            jsonBean = net.sf.json.JSONObject.fromObject("{\"p1\":\"" + violation.getDoctorName() +
                    violation.getParams() + "\"}");
        }

        msg = getMsg(jsonBean, msgId);

        BeanUtils.copyProperties(medical, violation);
        violation.setMedicalId(medical.getId()); // 门诊编号
        violation.setIcd10Code(medical.getDiagnosisIcd10()); // ICD10疾病编码
        violation.setDisease(medical.getDiagnosis()); // 诊断疾病
        violation.setMsgId(msgId); // 处方违规行为提醒消息Id
        violation.setMsgTxt(msg); // 处方违规行为提醒消息
        violation.setViolateTm(new Date()); // 处方违规时间
        violation.setViolateDt(new Date()); // 处方违规日期

        return violation;
    }

    /**
     * 取得消息内容
     */
    private String getMsg(JSONObject jsonBean, String msgId) {
        String msg;
        msg = clinicAuditMapper.selectMsg(msgId);
        @SuppressWarnings("unchecked")
        Iterator<Object> iterator = jsonBean.keys();
        while (iterator.hasNext()) {
            String key = iterator.next().toString();
            String sObj = jsonBean.getString(key);
            if (key.startsWith("p")) {
                key = key.replace("p", "");
            }
            msg = msg.replace("{" + key + "}", sObj);
        }
        return msg;
    }

    /**
     * 复制单条处方违规内容
     */
    private DtInvsViolationManageEntity copy(DtInvsViolationManageEntity orig) {
        DtInvsViolationManageEntity dest = new DtInvsViolationManageEntity();
        try {
            org.apache.commons.beanutils.BeanUtils.copyProperties(dest, orig);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return dest;
    }


    /**
     * 新生成消息队列
     */
    private void insertMessageQueue(DtHealthDataOutpatientMedicalEntity medical,
                                    List<DtInvsViolationManageEntity> violations) {
        List<DtInvsMessageQueueEntity> queues = new ArrayList<>();
        if (violations == null || violations.size() == 0) {
            DtInvsMessageQueueEntity queue = new DtInvsMessageQueueEntity();
            queue.setActionTm(new Date()); // 时间
            queue.setInstitutionId(medical.getInstitutionId()); // 机构ID
            queue.setSource(2); // 1：用户 2：系统
            queue.setBusinessSubType(401); // 业务子分类 401：处方
            queue.setFunctionKey(1); // 功能  1:登记  2：申诉
            queue.setMessageType(1); // 消息类型： 1：info 2:warning 3:error
            queue.setTargetId(medical.getId()); // 门诊病历表dt_health_data_outpatient_medical.id
            queues.add(queue);
        } else {
            for (DtInvsViolationManageEntity violation : violations) {
                DtInvsMessageQueueEntity queue = new DtInvsMessageQueueEntity();
                queue.setActionTm(violation.getViolateTm()); // 时间
                queue.setInstitutionId(medical.getInstitutionId()); // 机构ID
                queue.setSource(2); // 1：用户 2：系统
                queue.setBusinessSubType(401); // 业务子分类 401：处方
                queue.setFunctionKey(1); // 功能  1:登记  2：申诉
                queue.setMessageType(3); // 消息类型： 1：info 2:warning 3:error
                queue.setTargetId(violation.getId()); // 处方违规内容管理表dt_invs_violation_manage.id
                queues.add(queue);
            }
        }
        clinicAuditMapper.insertMessageQueue(queues);
    }

    /**
     * 新生成机构异常行为每日check
     */
    private List<DtInvsIllegalCheckDailyEntity> insertCheckDaily(DtHealthDataOutpatientMedicalEntity medical,
                                                                 List<DtInvsViolationManageEntity> violations) {
        List<DtInvsIllegalCheckDailyEntity> dailys = new ArrayList<>();
        for (DtInvsViolationManageEntity violation : violations) {
            DtInvsIllegalCheckDailyEntity daily = new DtInvsIllegalCheckDailyEntity();
            daily.setInstitutionId(medical.getInstitutionId());
            daily.setCheckDt(violation.getViolateTm()); // 检查日期
            daily.setBusinessType(40); // 业务分类 40：处方违规
            daily.setBusinessSubType(401); // 业务子分类 401：处方
            daily.setTargetId(violation.getId()); // 处方违规内容管理表dt_invs_violation_manage.id
            daily.setCheckSt(-1); // 检查状态 0：正常  -1：异常  更新途径：定时钟 及 卫监人员手工更新
            daily.setMsgId(violation.getMsgId()); // 处方违规提醒消息ID dt_invs_violation_manage.msg_id
            daily.setMessage(violation.getMsgTxt()); // 处方违规行为提醒消息
            daily.setId(IdGenerateUtil.idGenerateFromTime("04"));
            daily.setSourceType(4);
            dailys.add(daily);
        }
        clinicAuditMapper.insertCheckDaily(dailys);
        return dailys;
    }


    /**
     * 新生成机构异常行为申诉（申诉状态为关闭）
     */
    private void insertIllegalAppealClose(List<DtInvsIllegalCheckDailyEntity> dailys) {
        List<DtInvsIllegalAppealEntity> appeals = new ArrayList<>();
        for (DtInvsIllegalCheckDailyEntity daily : dailys) {
            DtInvsIllegalAppealEntity appeal = new DtInvsIllegalAppealEntity();
            appeal.setIllegalId(daily.getId()); // dt_invs_illegal_check_daily.id
            appeal.setAppealSt(11); // 申诉状态  0：未申诉  1：申诉中  -1：被驳回 2：申诉成功 10：关闭 11:不需要申诉
            appeal.setMessageSendSt(Short.parseShort("1")); // 0:未发送 1：已发送
            appeal.setId(IdGenerateUtil.idGenerateFromTime("04"));
            appeal.setSourceType(4);
            appeals.add(appeal);
        }
        clinicAuditMapper.insertIllegalAppeal(appeals);
    }

    /**
     * 新生成机构异常行为申诉
     */
    private void insertIllegalAppeal(List<DtInvsIllegalCheckDailyEntity> dailys) {
        List<DtInvsIllegalAppealEntity> appeals = new ArrayList<>();
        for (DtInvsIllegalCheckDailyEntity daily : dailys) {
            DtInvsIllegalAppealEntity appeal = new DtInvsIllegalAppealEntity();
            appeal.setIllegalId(daily.getId()); // dt_invs_illegal_check_daily.id
            appeal.setAppealSt(0); // 申诉状态  0：未申诉  1：申诉中  -1：被驳回 2：申诉成功 10：关闭
            appeal.setMessageSendSt(Short.parseShort("1")); // 0:未发送 1：已发送
            appeal.setId(IdGenerateUtil.idGenerateFromTime("04"));
            appeal.setSourceType(4);
            appeals.add(appeal);
        }
        clinicAuditMapper.insertIllegalAppeal(appeals);
    }


    /**
     * 判断医师是否有处方权
     */
    private DtInvsViolationManageEntity rightToPrescription(DdDoctorEntity doctor, List<ClinicAuditItemModel> items, String institutionId) {
        DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();

        // 医师职称
        String technicalTitle = doctor.getTechnicalTitle();
        if (!CheckUtil.isEmpty(technicalTitle)) {
            List<MaDataDictionaryEntity> technicalTitleList = dataDictionaryService.getCodeList(Constant.CODE_TECHNICAL_TITLE);
            List<MaDataDictionaryEntity> filterList = technicalTitleList.stream().filter(a -> (a.getValue() != null && a.getValue().equals(technicalTitle))).collect(Collectors.toList());
            if (filterList != null && filterList.size() > 0) {
                if (items.size() > 0) {
                    MaDataDictionaryEntity technical = filterList.get(0);
                    int val = Integer.parseInt(technical.getValue());
                    if (val > 5) { // 职称级别在助理医师以下
                        String param = "(" + technical.getFullName() + ")"; // 职称名
                        violation.setParams(param);
                        violation.setPrescriptionId(items.get(0).getWestItem().getPrescriptionId());
                        violation.setDoctorName(doctor.getName());
                        violation.setMsgId("820001");
                        return violation;
                    } else if (val > 4) { // 职称级别在医师以下
                        violation.setPrescriptionId(items.get(0).getWestItem().getPrescriptionId());
                        violation.setDoctorName(doctor.getName());
                        violation.setMsgId("820002");
                        return violation;
                    }
                }

            }
        }
        return null;
    }

    /**
     * 判断医师是否有精麻药品处方权
     */
    private DtInvsViolationManageEntity rightToAnesthetic(DdDoctorEntity doctor, List<ClinicAuditItemModel> items) {
        DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();

        // 麻醉药品和第一类精神药品处方资格
        Integer anestheticAuthority = doctor.getAnestheticAuthority();
        List<ClinicAuditItemModel> filterList = items.stream().filter(a -> (a.getBaseDrug() != null && a.getBaseDrug().getAnestheticFlg() != null && a.getBaseDrug().getAnestheticFlg() == 1)).collect(Collectors.toList());
        if (filterList != null && filterList.size() > 0) {
            if (anestheticAuthority == null || anestheticAuthority.intValue() != 1) {
                String param = "";
                for (ClinicAuditItemModel item : filterList) {
                    String name = item.getWestItem().getItemName(); // 精麻药品名
                    if (CheckUtil.isEmpty(param)) {
                        param = name;
                    } else {
                        param = param + "、" + name;
                    }
                }
                violation.setParams(param);
                violation.setPrescriptionId(items.get(0).getWestItem().getPrescriptionId());
                violation.setDoctorName(doctor.getName());
                violation.setMsgId("820003");
                return violation;
            }
        }

        return null;
    }

    /**
     * 判断医师是否有抗菌药物处方权
     */
    private DtInvsViolationManageEntity rightToAntibiosis(DdDoctorEntity doctor, List<ClinicAuditItemModel> items) {
        DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();

        // 抗菌药物处方资格
        Integer antibiosisAuthority = doctor.getAntibiosisAuthority();
        List<ClinicAuditItemModel> filterList = items.stream().filter(a -> (a.getBaseDrug() != null && a.getBaseDrug().getAntibiosisFlg() != null && a.getBaseDrug().getAntibiosisFlg() > 0)).collect(Collectors.toList());
        if (filterList != null && filterList.size() > 0) {
            if (antibiosisAuthority == null || antibiosisAuthority.intValue() < 1) {
                List<MaDataDictionaryEntity> antibiosisFlgList = dataDictionaryService.getCodeList(Constant.CODE_ANTIBIOSIS_FLG);
                String param = "";
                for (ClinicAuditItemModel item : filterList) {
                    MaInvsBaseDrugEntity baseDrug = item.getBaseDrug();
                    DtHealthDataWestPrescriptionEntity westItem = item.getWestItem();
                    List<MaDataDictionaryEntity> flgList = antibiosisFlgList.stream().filter(a -> (a.getValue() != null && baseDrug.getAntibiosisFlg() != null && a.getValue().equals(baseDrug.getAntibiosisFlg().toString()))).collect(Collectors.toList());
                    String name = getAntibiosisName(flgList, westItem, baseDrug);
                    if (CheckUtil.isEmpty(param)) {
                        param = name;
                    } else {
                        param = param + "、" + name;
                    }
                }
                violation.setParams(param);
                violation.setPrescriptionId(items.get(0).getWestItem().getPrescriptionId());
                violation.setDoctorName(doctor.getName());
                violation.setMsgId("820004");
                return violation;
            }
        }

        return null;
    }

    /**
     * 判断医师是否越级使用抗菌药物
     */
    private DtInvsViolationManageEntity rightToAntibiosisGrade(DdDoctorEntity doctor, List<ClinicAuditItemModel> items) {
        DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();

        // 抗菌药物处方资格
        Integer antibiosisAuthority = doctor.getAntibiosisAuthority();
        if (antibiosisAuthority == null || antibiosisAuthority.intValue() < 1) {
            return null;
        }
        List<ClinicAuditItemModel> filterList = items.stream().filter(a -> (a.getBaseDrug() != null && a.getBaseDrug().getAntibiosisFlg() != null && a.getBaseDrug().getAntibiosisFlg() > 1)).collect(Collectors.toList());
        if (filterList != null && filterList.size() > 0) {
            List<MaDataDictionaryEntity> antibiosisFlgList = dataDictionaryService.getCodeList(Constant.CODE_ANTIBIOSIS_FLG);
            String param = "";
            for (ClinicAuditItemModel item : filterList) {
                MaInvsBaseDrugEntity baseDrug = item.getBaseDrug();
                if (antibiosisAuthority.intValue() < baseDrug.getAntibiosisFlg().intValue()) {
                    DtHealthDataWestPrescriptionEntity westItem = item.getWestItem();
                    List<MaDataDictionaryEntity> flgList = antibiosisFlgList.stream().filter(a -> (a.getValue() != null && baseDrug.getAntibiosisFlg() != null && a.getValue().equals(baseDrug.getAntibiosisFlg().toString()))).collect(Collectors.toList());
                    String name = getAntibiosisName(flgList, westItem, baseDrug);
                    if (CheckUtil.isEmpty(param)) {
                        param = name;
                    } else {
                        param = param + "、" + name;
                    }
                }
            }
            if (!CheckUtil.isEmpty(param)) {
                violation.setParams(param);
                violation.setPrescriptionId(items.get(0).getWestItem().getPrescriptionId());
                violation.setDoctorName(doctor.getName());
                violation.setMsgId("820005");
                return violation;
            }
        }

        return null;
    }

    /**
     * 判断限制使用性别
     */
    private List<DtInvsViolationManageEntity> limitGender(List<ClinicAuditItemModel> items, Integer gender) {
        // 病历上患者性别没有填写时，不校验
        if (gender.intValue() == 0) {
            return null;
        }

        List<DtInvsViolationManageEntity> violations = new ArrayList<>();

        for (ClinicAuditItemModel item : items) {
            DtHealthDataWestPrescriptionEntity westItem = item.getWestItem();
            MaInvsBaseDrugEntity baseDrug = item.getBaseDrug();
            Integer limitGender = baseDrug.getLimitGender();
            if (CheckUtil.isEmpty(limitGender) || limitGender.intValue() == 0) {
                continue;
            }
            // 药品限制使用性别和患者性别不一致
            if (limitGender.intValue() != gender.intValue()) {
                String param = baseDrug.getLimitGenderTxt();
                DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();
                violation.setParams(param);
                violation.setPrescriptionId(items.get(0).getWestItem().getPrescriptionId());
                violation.setItemName(westItem.getItemName());
                violation.setMsgId("840005");
                violations.add(violation);
            }
        }

        if (violations.size() > 0) {
            return violations;
        }

        return null;
    }

    /**
     * 判断儿童禁用
     */
    private List<DtInvsViolationManageEntity> childForbid(List<ClinicAuditItemModel> items, Integer age) {
        // 病历上患者年龄没有填写或者年龄大于14岁时，不校验
        if (age == null || age.intValue() > 14) {
            return null;
        }

        List<DtInvsViolationManageEntity> violations = new ArrayList<>();

        for (ClinicAuditItemModel item : items) {
            DtHealthDataWestPrescriptionEntity westItem = item.getWestItem();
            MaInvsBaseDrugEntity baseDrug = item.getBaseDrug();
            Integer childForbid = baseDrug.getChildForbid();
            if (childForbid.intValue() == 0) {
                continue;
            }
            // 儿童禁用
            if (childForbid.intValue() == 1) {
                String param = "儿童禁用";
                DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();
                violation.setParams(param);
                violation.setPrescriptionId(items.get(0).getWestItem().getPrescriptionId());
                violation.setItemName(westItem.getItemName());
                violation.setMsgId("840007");
                violations.add(violation);
            }
        }

        if (violations.size() > 0) {
            return violations;
        }

        return null;
    }


    /**
     * 判断老年患者禁用
     */
    private List<DtInvsViolationManageEntity> oldForbid(List<ClinicAuditItemModel> items, Integer age) {
        // 病历上患者年龄没有填写或者年龄小于60岁时，不校验
        if (age == null || age.intValue() < 60) {
            return null;
        }

        List<DtInvsViolationManageEntity> violations = new ArrayList<>();

        for (ClinicAuditItemModel item : items) {
            DtHealthDataWestPrescriptionEntity westItem = item.getWestItem();
            MaInvsBaseDrugEntity baseDrug = item.getBaseDrug();
            Integer oldForbid = baseDrug.getOldForbid();
            if (oldForbid.intValue() == 0) {
                continue;
            }
            // 老年患者禁用
            if (oldForbid.intValue() == 1) {
                String param = "老年患者禁用";
                DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();
                violation.setParams(param);
                violation.setPrescriptionId(items.get(0).getWestItem().getPrescriptionId());
                violation.setItemName(westItem.getItemName());
                violation.setMsgId("840008");
                violations.add(violation);
            }
        }

        if (violations.size() > 0) {
            return violations;
        }

        return null;
    }

    /**
     * 判断是否超剂量用药
     */
    private List<DtInvsViolationManageEntity> overDosage(List<ClinicAuditItemModel> items) {
        List<DtInvsViolationManageEntity> violations = new ArrayList<>();

        for (ClinicAuditItemModel item : items) {
            DtHealthDataWestPrescriptionEntity westItem = item.getWestItem();
            MaInvsBaseDrugEntity baseDrug = item.getBaseDrug();
            // 处方剂量
            if(ObjectUtils.isEmpty(westItem.getOnceDose())){
                continue;
            }
            BigDecimal onceDose = new BigDecimal(((Number) westItem.getOnceDose()).toString());
            // 核心药品表最大用量
            BigDecimal maxQuantity = baseDrug.getMaxQuantity();
            if (onceDose == null || maxQuantity == null) {
                continue;
            }
            // 如果是按制剂单位开的话，需要转换成剂量单位   add by zxb 2020.3.25   start -------------------------------
            if (westItem.getOnceDoseUnitType() != null && westItem.getOnceDoseUnitType().intValue() == 1) {
                // 基础表中含量规格没有存的情况下,退出
                if (baseDrug.getContentSpec() == null) {
                    continue;
                }
                // 根据制剂单位转化成剂量单位
                onceDose = onceDose.multiply(baseDrug.getContentSpec());
            } //add by zxb 2020.3.25   end -------------------------------

            // 处方剂量超出核心药品表最大用量
            if (maxQuantity.compareTo(onceDose) == -1) {
                String param = maxQuantity + baseDrug.getContentUnitText();
                DtInvsViolationManageEntity violation = new DtInvsViolationManageEntity();
                violation.setParams(param);
                violation.setPrescriptionId(items.get(0).getWestItem().getPrescriptionId());
                violation.setItemName(westItem.getItemName());
                violation.setMsgId("840004");
                violations.add(violation);
            }
        }

        if (violations.size() > 0) {
            return violations;
        }

        return null;
    }

    /**
     * 取得包含级别的抗菌药物名称
     */
    private String getAntibiosisName(List<MaDataDictionaryEntity> flgList
            , DtHealthDataWestPrescriptionEntity westItem
            , MaInvsBaseDrugEntity baseDrug) {
        String name = "";
        if (flgList != null && flgList.size() > 0) {
            name = westItem.getItemName() + "(" + flgList.get(0).getAbbreviation() + ")"; // 药物名+(抗菌药物等级)
        } else {
            throw new RuntimeException("ma_data_dictionary表中无抗菌药物等级[" + baseDrug.getAntibiosisFlg() + "]数据，请设置！");
        }
        return name;
    }

    /**
     * 更新病历处方违规内容
     * 先删除再重新生成
     */
    private void replacePrescriptionViolation(DtHealthDataOutpatientMedicalEntity medical,
                                              List<DtHealthDataWestPrescriptionEntity> prescriptions,
                                              List<DtInvsViolationManageEntity> violations,
                                              DtHealthManageInstitutionEntity institution) {
        for (DtHealthDataWestPrescriptionEntity prescription : prescriptions) {
            DtInvsViolationManageEntity w = new DtInvsViolationManageEntity();
            w.setInstitutionId(medical.getInstitutionId());
            w.setMedicalId(medical.getId());
            w.setPrescriptionId(prescription.getPrescriptionId());
            // 旧的消息队列状态更新成不显示
            List<Integer> targetIds = clinicAuditMapper.selectTargetIds(w);
            if (targetIds.size() > 0) {
                clinicAuditMapper.updateQueueStatus(medical.getInstitutionId(), 401, 3, targetIds);
                // 旧的异常行为每日check更新成已删除
                List<Integer> illegalIds = clinicAuditMapper.selectIllegalIds(medical.getInstitutionId(), 40, 401, targetIds);
                clinicAuditMapper.updateCheckDailyStatus(medical.getInstitutionId(), 40, 401, targetIds);
                // 旧的机构异常行为申诉更新成已删除
                if (illegalIds.size() > 0) {
                    clinicAuditMapper.updateIllegalAppealStatus(illegalIds);
                }
            }
            // 旧的违规内容状态更新成已删除
            clinicAuditMapper.updateStatus(w);
        }
        // 新生成违规内容
        if (violations.size() > 0) {
            //获取医疗机构所在卫监所，同时判断该卫监是否对所有处方违规不需要申诉，is_pres_appeal=0 不需要申诉 is_pres_appeal=1需要申诉
            String isPresAppeal = dtHealthManageInstitutionMapper.selectPresAppeal(institution.getId());
            violations.forEach(item -> {
                item.setId(IdGenerateUtil.idGenerateFromTime("04"));
                item.setSourceType(4);
            });
            try {
                clinicAuditMapper.insertPrescriptionViolation(violations);
                // 新生成消息队列(用于首页地图展示)
                insertMessageQueue(medical, violations);
                // 新生成机构异常行为每日check
                List<DtInvsIllegalCheckDailyEntity> dailys = insertCheckDaily(medical, violations);
                // 生成新的机构异常行为申诉
                insertIllegalAppealHandle(dailys, isPresAppeal, institution);
            } catch (Exception e) {
            }
        }

    }

    /**
     * 新增申诉数据
     */
    private void insertIllegalAppealHandle(List<DtInvsIllegalCheckDailyEntity> dailys, String isPresAppeal, DtHealthManageInstitutionEntity institutionEntity) {
        List<String> msgIds = new ArrayList<>();
        for (DtInvsIllegalCheckDailyEntity daily : dailys) {
            msgIds.add(daily.getMsgId());
        }
        List<DtInvsRuleWarningEntity> ruleWarning = dtInvsRuleWarningMapper.selectWhite(msgIds, institutionEntity);
        for (DtInvsIllegalCheckDailyEntity daily : dailys) {
            //获取check_daily数据的messageId和ruleWarning中的数据通过messageId一一对应
            String msgId = daily.getMsgId();
            //warningFlag初始化（不属于任何一个卫监所监管的诊所用）
            if (institutionEntity.getAuditHint() == 1) {
                daily.setWarningFlag(1);
            } else {
                daily.setWarningFlag(0);
            }
            //循环处方违规规则
            for (int i = 0; i < ruleWarning.size(); i++) {
                //msgId和ruleWarning的ruleId一一对应
                if (msgId.equals(ruleWarning.get(i).getRuleId())) {
                    //判断该处方规则是否开启预警
                    if (daily.getWarningFlag() == null && ruleWarning.get(i).getIsWarning() == 1 && institutionEntity.getAuditHint() == 1 && Objects.equals(isPresAppeal, "1")) {
                        daily.setWarningFlag(1);//1:该处方规则开启预警
                    } else {
                        daily.setWarningFlag(0);//0:该处方规则关闭预警
                    }

                    List<DtInvsRuleWhiteInstitutionEntity> whiteInstitution = ruleWarning.get(i).getRuleWhite();
                    //判断该机构是否在白名单中
                    if (whiteInstitution != null && whiteInstitution.size() > 0) {
                        for (DtInvsRuleWhiteInstitutionEntity institution : whiteInstitution) {
                            if (institution.getInstitutionId().equals(dailys.get(0).getInstitutionId())) {
                                daily.setWarningFlag(2); //2:该处方规则的白名单中有该机构
                                break;
                            }
                        }
                    }
                    break;
                }
            }

        }
        //该机构处于白名单中的处方规则
        List<DtInvsIllegalCheckDailyEntity> checkDailyWhite = dailys.stream().filter(a -> a.getWarningFlag() == 2).
                collect(Collectors.toList());
        //预警提醒的处方规则
        List<DtInvsIllegalCheckDailyEntity> checkDailyWarning = dailys.stream().filter(a -> a.getWarningFlag() == 1).
                collect(Collectors.toList());
        //关闭预警提醒的监管规则
        List<DtInvsIllegalCheckDailyEntity> checkDailyNotWarning = dailys.stream().filter(a -> a.getWarningFlag() == 0).
                collect(Collectors.toList());
        if (checkDailyWhite != null && checkDailyWhite.size() > 0) {
            insertIllegalAppealWhite(checkDailyWhite);
        }
        if (checkDailyNotWarning != null && checkDailyNotWarning.size() > 0) {
            insertIllegalAppealClose(checkDailyNotWarning);
        }
        if (checkDailyWarning != null && checkDailyWarning.size() > 0) {
            insertIllegalAppeal(checkDailyWarning);
        }
    }

    /**
     * 该机构是白名单，appeal-st为12
     */
    private void insertIllegalAppealWhite(List<DtInvsIllegalCheckDailyEntity> dailys) {
        List<DtInvsIllegalAppealEntity> appeals = new ArrayList<>();
        for (DtInvsIllegalCheckDailyEntity daily : dailys) {
            DtInvsIllegalAppealEntity appeal = new DtInvsIllegalAppealEntity();
            appeal.setIllegalId(daily.getId()); // dt_invs_illegal_check_daily.id
            appeal.setAppealSt(12); // 申诉状态  0：未申诉  1：申诉中  -1：被驳回 2：申诉成功 10：关闭 11:不需要申诉 12:白名单
            appeal.setMessageSendSt(Short.parseShort("1")); // 0:未发送 1：已发送
            appeal.setId(IdGenerateUtil.idGenerateFromTime("04"));
            appeal.setSourceType(4);
            appeals.add(appeal);
        }
        clinicAuditMapper.insertIllegalAppeal(appeals);
    }

    /**
     * 复制多条处方违规内容
     */
    private List<DtInvsViolationManageEntity> copys(List<DtInvsViolationManageEntity> origs) {
        List<DtInvsViolationManageEntity> dests = new ArrayList<>();
        for (DtInvsViolationManageEntity orig : origs) {
            dests.add(copy(orig));
        }
        return dests;
    }

    /**
     * @param baseDrugs       待匹配药品列表
     * @param name            药品名称
     * @param producer        生产厂家
     * @param specifications  规格
     * @param conversionRatio 转换比
     * @return 匹配的药品id
     */

    public List<Integer> getMatchedIds(List<MaInvsBaseDrugEntity> baseDrugs, String name, String producer, String specifications, String conversionRatio) {

        InnerEntity inner = new InnerEntity(name, producer, specifications, conversionRatio, split(name), split(producer));

        Map<Integer, Integer> map = new TreeMap<>();
        baseDrugs.forEach(entity -> map.put(entity.getId(), score(entity, inner)));
        List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
        list.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));

        List<Integer> idList = new ArrayList<>();
        for (int i = 0; i < (Math.min(10, list.size())); i++) {
            idList.add(list.get(i).getKey());
        }
        return idList;
    }

    /**
     * 算分
     *
     * @param entity      每条药品数据
     * @param innerEntity 存放查询信息的内部类
     * @return 算分结果
     */
    public static int score(MaInvsBaseDrugEntity entity, InnerEntity innerEntity) {
        int nameScore = scoreName(entity, innerEntity);
        int producerScore = scoreProducer(entity, innerEntity);
        if (nameScore + producerScore < 50) {
            return nameScore + producerScore;
        }
        return nameScore + producerScore + scoreSpecifications(entity, innerEntity) + scoreConversionRatio(entity, innerEntity);
    }

    private static int scoreName(MaInvsBaseDrugEntity entity, InnerEntity innerEntity) {
        if (org.apache.commons.lang.StringUtils.isEmpty(innerEntity.getName())) {
            return 100;
        }
        if (org.apache.commons.lang.StringUtils.equals(entity.getName(), innerEntity.getName())) {
            return 150;
        }
        int count = (int) innerEntity.getSplitName().stream().filter(s -> entity.getName().contains(s)).count();
        return count * 100 / innerEntity.getSplitName().size();
    }

    private static int scoreProducer(MaInvsBaseDrugEntity entity, InnerEntity innerEntity) {
        if (org.apache.commons.lang.StringUtils.isEmpty(innerEntity.getProducer())) {
            return 100;
        }
        if (org.apache.commons.lang.StringUtils.equals(entity.getManufacturer(), innerEntity.getProducer())) {
            return 150;
        }
        int count = (int) innerEntity.getSplitProducer().stream().filter(s -> entity.getManufacturer().contains(s)).count();
        return count * 100 / innerEntity.getSplitProducer().size();
    }

    private static int scoreSpecifications(MaInvsBaseDrugEntity entity, InnerEntity innerEntity) {
        if (org.apache.commons.lang.StringUtils.isEmpty(innerEntity.getSpecifications())) {
            return 0;
        }
        String[] split = innerEntity.getSpecifications().split("\\D+");
        int score = 0;
        List<String> stringList = Arrays.asList(entity.getSpecification().split("\\D+"));
        for (String s : split) {
            if (stringList.contains(s)) {
                score += 100 / split.length;
            }
        }
        return score;
    }

    private static int scoreConversionRatio(MaInvsBaseDrugEntity entity, InnerEntity innerEntity) {
        if (org.apache.commons.lang.StringUtils.isEmpty(innerEntity.getConversionRatio())) {
            return 0;
        }
        return org.apache.commons.lang.StringUtils.equals(entity.getContentSpec().toString(), innerEntity.getConversionRatio()) ? 50 : -50;
    }

    /**
     * 二元分词
     *
     * @param str 药品名称/生产厂家
     * @return 分词列表
     */
    public static List<String> split(String str) {
        List<String> list = new ArrayList<>();
        if (org.apache.commons.lang.StringUtils.isEmpty(str)) {
            return list;
        }
        String s = str.replaceAll(STOP_WORD, "");
        if (s.length() < 3) {
            list.add(s);
            return list;
        }
        for (int i = 0; i < s.length() - 1; i++) {
            list.add(s.charAt(i) + String.valueOf(s.charAt(i + 1)));
        }
        return list;
    }


    /**
     * 存放查询信息的内部类
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private static class InnerEntity {
        private String name;
        private String producer;
        private String specifications;
        private String conversionRatio;
        //        private String form;
//
//        private List<String> splitForm;
        private List<String> splitName;
        private List<String> splitProducer;
    }

}
